home *** CD-ROM | disk | FTP | other *** search
- Path: uunet!rs
- From: rs@uunet.UU.NET (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v10i098: A /dev/fd device driver for 4.3 and NFS systems
- Message-ID: <790@uunet.UU.NET>
- Date: 7 Aug 87 11:24:25 GMT
- Organization: UUNET Communications Services, Arlington, VA
- Lines: 660
- Approved: rs@uunet.UU.NET
-
- Submitted-by: emory!arnold (Arnold D. Robbins {EUCC})
- Posting-number: Volume 10, Issue 98
- Archive-name: dev.fd
-
- [ The code here implements /dev/fd/0, /dev/fd/12, etc., so that a program
- can treat its file descriptors as files; the README-FIRST describes
- some possible tricks that one can now play with, e.g., KSH, as a result
- of this. --r$ ]
-
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # README-FIRST
- # README
- # fd.4
- # dev_fd.c
- # PATCH
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'README-FIRST'" '(2045 characters)'
- if test -f 'README-FIRST'
- then
- echo shar: will not over-write existing file "'README-FIRST'"
- else
- cat << \SHAR_EOF > 'README-FIRST'
- README-FIRST
-
- The files in this directory contain the source code for the "fd"
- pseudo-device driver for 4.[23] BSD Unix. The idea originated with the Eighth
- Edition Research Unix system.
- The original version for BSD Unix was done by Fred Blonder at the
- University of Maryland. He simply had a bunch of devices, /dev/fd0 - /dev/fd19.
- The Research Unix system uses a /dev/fd directory, with files 0 - 19.
- The latter version of the mechanism is used by the Korn shell (ksh)
- for "process substitution", a feature whereby programs can read/write files
- which are really pipes from/to other processes. This mechanism allows one
- to have pipelines that move data in more than one dimension. Once this
- driver is installed, the ksh needs to be recompiled. Process substitution
- is new in the version of the ksh known as "ksh-i". I don't know if the older
- ksh had it as an undocumented feature.
- To allow the ksh to use the fd driver, and for compatibility with
- Research Unix, I have changed the fd.4 man page to refer to the /dev/fd
- directory.
-
- This version of the fd driver now includes code for NFS systems.
- Unfortunately, for reasons detailed in the source code, the NFS version
- requires a small patch to the stock system code. The file that is affected
- is /sys/specfs/spec_vnodeops.c. This patch is included in the file PATCH.
- This patch should not affect any other system code (famous last words!).
- Besides the NFS version, I have added some additional error checking, and
- a bug fix to the inode version as well. This is commented, so if someone
- wants the exact original code, they can change it back.
-
- The NFS version works without change on both Sun systems running
- SunOS 3.3, and Mt. Xinu 4.3 + NFS 3.2. On the Sun, "make depend" will not
- work because of where the inode.h file is included from if NFS is not
- defined. This is not a very big deal, just comment out that line. The
- interaction with the file locking mechanisms is not clear. (i.e. I didn't
- bother to investigate it.)
-
- Arnold Robbins
- Emory University Computing Center
- July, 1987
- SHAR_EOF
- chmod +x 'README-FIRST'
- fi # end of overwriting check
- echo shar: extracting "'README'" '(1781 characters)'
- if test -f 'README'
- then
- echo shar: will not over-write existing file "'README'"
- else
- cat << \SHAR_EOF > 'README'
- From fred@gymble.UUCP (Fred Blonder) Fri Jun 21 00:34:53 1985
- Relay-Version: version B 2.10.2 9/18/84; site gatech.CSNET
- Posting-Version: version B 2.10.1 6/24/83; site gymble.UUCP
- Path: gatech!akgua!whuxlm!whuxl!houxm!ihnp4!mhuxn!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!umcp-cs!gymble!fred
- From: fred@gymble.UUCP (Fred Blonder)
- Newsgroups: net.sources
- Subject: ``file descriptor'' pseudo-device driver for 4.2BSD
- Message-ID: <173@gymble.UUCP>
- Date: 21 Jun 85 04:34:53 GMT
- Date-Received: 23 Jun 85 03:29:53 GMT
- Distribution: net
- Organization: U of Maryland, Laboratory for Parallel Computation, C.P., MD
- Lines: 272
-
- I'm re-posting this due to popular demand:
-
- This is a pseudo-device driver which implements a
- series of devices: /dev/fd0 - /dev/fd19 which are
- surreptitiously equivalenced to whatever file (if
- any) is associated with the corresponding file
- descriptor in the process which is attempting to
- open that file. In other words: opening the file
- /dev/fd<n> is almost the same as doing the system
- call: dup(n). It's different in that if the file
- is a device, the driver's open routine is called.
- If the corresponding file descriptor is a pipe
- (socket) a dup() sys call is faked.
-
- This can be used to force programs to read from
- their standard input, or write to their standard
- output, even if they haven't been designed to do
- so. For instance:
-
- ln -s /dev/fd0 xx.c
- cc xx.c
- { Type a C program here. }
- ^D
- a.out
-
- Or, to see if a certain source file compiled into a
- particular object file:
-
- cc source.c -o /dev/fd1 | cmp - objectfile
-
- Be sure to make these mods in your conf directory.
-
- In the config file:
- pseudo-device fd
-
- In the ``files'' file:
- sys/dev_fd.c optional fd
-
- <**************************************************************>
- SHAR_EOF
- chmod +x 'README'
- fi # end of overwriting check
- echo shar: extracting "'fd.4'" '(3615 characters)'
- if test -f 'fd.4'
- then
- echo shar: will not over-write existing file "'fd.4'"
- else
- cat << \SHAR_EOF > 'fd.4'
- ...
- ... $Header: fd.4,v 1.3 87/07/10 10:20:26 root Locked $
- ...
- ... $Log: fd.4,v $
- ... Revision 1.3 87/07/10 10:20:26 root
- ... Added ERRORS section. ADR.
- ...
- ... Revision 1.2 87/07/05 12:05:49 root
- ... Added synopsis entry, changed for /dev/fd as a directory, added notes
- ... about ksh, /dev/std{in, out, err} and only having 20 entries. ADR.
- ...
- ... Revision 1.1 87/07/02 15:28:01 root
- ... Initial revision
- ...
- ...
- .TH FD 4 EUCC
- .UC 4
- .SH NAME
- /dev/fd/* \- ``file descriptor'' driver
- .SH SYNOPSIS
- .B "pseudo-device fd"
- .SH DESCRIPTION
- .B /dev/fd/0
- -
- .B /dev/fd/19
- are special files that reference the files associated with a process'
- open file descriptors.
- That is, opening the file: \fB/dev/fd/\fIN\fR is equivalent to opening whatever
- file you opened to get the file descriptor
- .IR N ,
- even though you may not
- know the file's true name (or even if it has one).
- This can be used to force a program which opens files by name, to connect
- itself to open file descriptors (which you have thoughtfully provided) in
- weird and wonderful ways. A simple use would be to force a program which
- only accepts input from files, to read from its standard input.
- For instance:
- .sp
- .ti +10
- cat /dev/fd/0
- .sp
- produces the same result as:
- .sp
- .ti +10
- cat -
- .sp
- which would be useful if
- .IR cat (1)
- didn't use the ``-'' convention.
- .PP
- This driver is particularly useful for enabling the ``process substitution''
- mechanism in
- .IR ksh (1).
- .PP
- If the open file descriptor references a socket, the driver fakes a
- .IR dup (2)
- system call instead of actually opening a file. In this case it
- checks to see that the read/write mode you are attempting to open
- the file with, is compatible with the mode of the existing file descriptor.
- That is, if descriptor 5 refers to a socket and
- is open for writing, you cannot open
- .B /dev/fd/5
- for reading.
- .PP
- As a notational convenience, the files
- .BR /dev/stdin ,
- .BR /dev/stdout ,
- and
- .B /dev/stderr
- are provided as hard links to
- .BR /dev/fd/0 ,
- .BR /dev/fd/1 ,
- and
- .BR /dev/fd/2
- respectively.
- .SH ERRORS
- The named file descriptor will be ``opened'' as described above unless
- one of the following is true.
- .PP
- .TP 15
- [ENXIO]
- The file descriptor attempting to be reopened is larger than the
- maximum number of open files that a process may have.
- (E.g., attempting to open file descriptor 65 on a 4.3BSD system.)
- .TP 15
- [EBADF]
- The file descriptor attempting to be reopened is not an open file.
- .PP
- Other errors as would be returned by
- .IR access (2),
- and
- .IR open (2).
- .SH SEE ALSO
- .IR ksh (1),
- .IR access (2),
- .IR dup (2),
- .IR open (2).
- .SH AUTHOR
- Fred Blonder <Fred@mimsy.umd.EDU>
- .br
- NFS version by Arnold Robbins <arnold@emory.EDU>
- .SH FILES
- .BR /dev/fd/* ,
- .BR /dev/stdin ,
- .BR /dev/stdout ,
- .B /dev/stderr
- .SH BUGS
- Since, for sockets,
- the driver fakes a
- .I dup
- system call rather than actually re-opening
- the file, the new descriptor is a copy of the
- .IR dup 'ed
- descriptor, and shares
- its properties. Specifically: it will have the same read/write mode as the
- .IR dup 'ed
- descriptor. If descriptor 0 is open for reading and writing, opening
- .B /dev/fd/0
- for reading will return a writable, as well as readable, file
- descriptor.
- Also: the descriptors share the same read/write pointer, so seeks, reads and
- writes on one will affect the other.
- .PP
- While not a bug in the driver specifically, use of these files can produce odd
- interactions with programs which don't expect to have their file
- descriptors surreptitiously aliased.
- .PP
- Having only 20 entries in the
- .B /dev/fd
- directory is an anachronism; modern Unix systems allow
- a process to have many more open file descriptors.
- SHAR_EOF
- chmod +x 'fd.4'
- fi # end of overwriting check
- echo shar: extracting "'dev_fd.c'" '(8981 characters)'
- if test -f 'dev_fd.c'
- then
- echo shar: will not over-write existing file "'dev_fd.c'"
- else
- cat << \SHAR_EOF > 'dev_fd.c'
- #ifndef lint
- static char rcsid[] = "@(#)$Header: dev_fd.c,v 1.6 87/07/10 10:24:13 root Locked $";
- #endif lint
-
- /*
- * fd.c Fred Blonder - U of Maryland 11-Sep-1984
- *
- * ``File Descriptor'' pseudo-device driver, rewritten for Berkeley 4.2.
- *
- * Opening minor device N opens the file (if any) connected to file-descriptor
- * N belonging to the calling process. Note that this driver consists of only
- * the ``open()'' routine, because all subsequent references to this file will
- * be direct to the other driver.
- *
- * NFS version by
- * Arnold Robbins -- Emory University Computing Center -- Summer 87
- */
-
- /*
- * $Log: dev_fd.c,v $
- * Revision 1.6 87/07/10 10:24:13 root
- * Removed debugging printfs. ADR.
- *
- * Revision 1.5 87/07/10 10:20:12 root
- * Added ENXIO check to inode version. ADR.
- *
- * Revision 1.4 87/07/05 14:19:41 root
- * Added NOFILE/ENXIO check, some minor cleanup. ADR.
- *
- * Revision 1.3 87/07/05 10:46:48 root
- * Brought the comments into sync with reality. Bug fix to inode version. ADR.
- *
- * Revision 1.2 87/07/03 16:53:46 root
- * NFS version of the driver. Works just fine on a sun. ADR.
- *
- * Revision 1.1 84/12/01 21:38:17 chris
- * Initial revision
- *
- */
-
- #include "fd.h"
- #if NFD > 0
-
- #include "../h/param.h"
- #ifdef NFS
- #include "../h/time.h"
- #include "../h/vnode.h"
- #else
- #include "../h/inode.h"
- #endif
- #include "../h/file.h"
- #include "../h/dir.h"
- #include "../h/user.h"
- #include "../h/errno.h"
-
- /*
- * THIS CODE NEEDS CLEANING AS SOON AS ASSIGNMENTS TO u.u_* GO AWAY
- */
-
- /*
- * The NFS mods are so extensive that I have decided to provide two whole
- * copies of the routine, one for NFS and one for regular BSD, instead
- * of mixing them up with ifdefs. The non-NFS code is the original
- * version from UMD. A.D.R.
- */
-
- #ifdef NFS
- /*
- * XXX
- *
- * WARNING!!!!! This piece of code requires that a patch be made
- * to the stock NFS 3.2 code in specfs/spec_vnodeops.c$spec_open().
- * The modification is to pass a pointer to the vnode for this file
- * into this routine in the call to (*cdewsw[major(dev)])(), as a fourth
- * argument.
- *
- * Why? you ask. When this device is opened, open() calls copen() which calls
- * vn_open(). Now, the whole idea behind this "device" is to substitute an
- * already open file for this one. The way to do this is to hand back up a vnode
- * for said open file. The regular inode version of this code has it easy.
- * The file structure for this device points at the device's inode. Chuck
- * that inode and substitute the inode of the already open file. It is not
- * so easy in the NFS case, because *the file structure does not yet
- * point at a vnode*. That is only done in copen() after the vn_open()
- * completes. Right now, we're still in the middle of the open. So we have
- * no way of getting at the original vnode unless it is passed in to us.
- * So that is why spec_open() has to pass a pointer to the vnode (pointer)
- * to us, so we can switch it around.
- */
-
- fdopen (dev, mode, newdev, vpp)
- dev_t dev;
- int mode;
- dev_t *newdev;
- struct vnode **vpp; /* vnode for this device */
- {
- struct file *fp, *wfp;
- struct vnode *vp, *wvp;
- int vmode = 0;
- int rwmode, error;
-
- if (minor(dev) >= NOFILE) /* sanity check */
- return (ENXIO);
-
- *newdev = dev; /* XXX - force loop termination in spec_open() */
-
- /*
- * Note the horrid kludge here: u.u_r.r_val1 contains the value
- * of the new file descriptor, which has not been disturbed since
- * it was allocated.
- */
-
- if ((fp = getf(u.u_r.r_val1)) == NULL)
- return (u.u_error);
-
- if ((wfp = getf(minor(dev))) == NULL)
- return (u.u_error);
-
- /*
- * We must explicitly test for this case because ufalloc() may
- * have allocated us the same file desriptor we are referring
- * to, if the proccess referred to an invalid (closed) descriptor.
- * Ordinarily this would be caught by getf(), but by the time we
- * reach this routine u_pofile[minor(dev)] could already be set
- * to point to our file struct.
- */
- if (fp == wfp)
- return (EBADF);
-
- vp = *vpp;
-
- /*
- * Fake a ``dup()'' sys call if it isn't a vnode.
- */
- if (wfp->f_type != DTYPE_VNODE) {
- /*
- * Check that the mode the file is being opened
- * for is consistent with the mode of the existing
- * descriptor. This isn't as clean as it should be,
- * but this entire driver is a real kludge anyway.
- */
- rwmode = mode & (FREAD|FWRITE);
- if ((wfp->f_flag & rwmode) != rwmode)
- return (EACCES);
-
- /* Delete references to this pseudo-device. */
- VN_RELE(vp); /* Chuck the vnode. */
- fp->f_count = 0; /* Chuck the file structure. */
- crfree(fp->f_cred);
- /* Dup the file descriptor. */
- dupit(u.u_r.r_val1, wfp, u.u_pofile[minor(dev)]);
- *vpp = (struct vnode *)wfp->f_data; /* needed? */
- return (0);
- }
-
- /*
- * now have a regular vnode.
- */
- error = 0;
- wvp = (struct vnode *)wfp->f_data;
-
- /*
- * Since we're opening a file again, we run through all the
- * permission checks so this can't be used as a loophole to
- * get access to a file we shouldn't have. (GROT)
- */
- if (mode & FREAD && (error = VOP_ACCESS(wvp, VREAD, u.u_cred)))
- goto bad;
- if (mode & (FWRITE|FTRUNC)) {
- if (vp->v_type == VDIR) {
- error = EISDIR;
- goto bad;
- }
- if ((error = VOP_ACCESS(wvp, VWRITE, u.u_cred)))
- goto bad;
- }
-
- /*
- * The file must always exist, so we don't even bother testing
- * for its presence.
- */
- if ((mode & (FCREAT|FEXCL)) == (FCREAT|FEXCL)) {
- error = EEXIST;
- goto bad;
- }
-
- /*
- * This may not make any sense, but I'm paranoid and figure that
- * it's probably an error.
- */
- if (mode & FTRUNC) {
- error = EBUSY;
- goto bad;
- }
-
- /* Call the device-specific open routine, if any. */
- vmode = mode & ~(FCREAT | FEXCL);
- if (wvp->v_type != VREG &&
- (error = VOP_OPEN(&wvp, vmode, u.u_cred)) != 0)
- goto bad;
-
- /*
- * Made it this far, now return the other vnode back up the
- * call chain for insertion into the file table entry.
- */
- VN_RELE(vp); /* We don't need this anymore. */
- wvp->v_count++;
- *vpp = wvp;
- return (0);
-
- bad:
- return (error);
- }
-
- #else /* ! NFS */
-
- fdopen(dev, mode)
- dev_t dev;
- int mode;
- {
- struct file *fp, *wfp;
- struct inode *ip, *wip;
- int rwmode, error;
-
- /* this check added by ADR */
- if (minor(dev) >= NOFILE) /* sanity check */
- return (ENXIO);
-
- /*
- * Note the horrid kludge here: u.u_r.r_val1 contains the value
- * of the new file descriptor, which has not been disturbed since
- * it was allocated.
- */
- if ((fp = getf(u.u_r.r_val1)) == NULL)
- return (u.u_error);
-
- if ((wfp = getf(minor(dev))) == NULL)
- return (u.u_error);
-
- /*
- * We must explicitly test for this case because ufalloc() may
- * have allocated us the same file desriptor we are referring
- * to, if the proccess referred to an invalid (closed) descriptor.
- * Ordinarily this would be caught by getf(), but by the time we
- * reach this routine u_pofile[minor(dev)] could already be set
- * to point to our file struct.
- */
- if (fp == wfp)
- return (EBADF);
-
- ip = (struct inode *)fp->f_data;
-
- /*
- * Fake a ``dup()'' sys call if it isn't an inode.
- */
- if (wfp->f_type != DTYPE_INODE) {
- /*
- * Check that the mode the file is being opened
- * for is consistent with the mode of the existing
- * descriptor. This isn't as clean as it should be,
- * but this entire driver is a real kludge anyway.
- */
- rwmode = mode & (FREAD|FWRITE);
- /* ADR: Bug fix: wfp below was originally fp */
- if ((wfp->f_flag & rwmode) != rwmode)
- return (EACCES);
-
- /* Delete references to this pseudo-device. */
- irele(ip); /* Chuck the inode. */
- fp->f_count = 0; /* Chuck the file structure. */
- /* Dup the file descriptor. */
- dupit(u.u_r.r_val1, wfp, u.u_pofile[minor(dev)]);
- return (0);
- }
-
- error = 0;
- wip = (struct inode *)wfp->f_data;
-
- /*
- * I'm not sure that we really need to lock the inode here,
- * but why not be paranoid?
- */
- ilock(wip);
-
- /*
- * Since we're opening a file again, we run through all the
- * permission checks so this can't be used as a loophole to
- * get access to a file we shouldn't have. (GROT)
- */
- if (mode & FREAD && access(wip, IREAD))
- goto bad;
- if (mode & (FWRITE|FTRUNC)) {
- if ((ip->i_mode&IFMT) == IFDIR) {
- error = EISDIR;
- goto bad;
- }
- if (access(wip, IWRITE))
- goto bad;
- }
-
- /*
- * The file must always exist, so we don't even bother testing
- * for its presence.
- */
- if ((mode & (FCREAT|FEXCL)) == (FCREAT|FEXCL)) {
- error = EEXIST;
- goto bad;
- }
-
- /*
- * This may not make any sense, but I'm paranoid and figure that
- * it's probably an error.
- */
- if (mode & FTRUNC) {
- error = EBUSY;
- goto bad;
- }
-
- /* Call the device-specific open routine, if any. */
- if ((error = openi(wip, mode)) != 0)
- goto bad;
-
- /*
- * Made it this far, now switch the inode pointers in the
- * file descriptors around, to make this file open refer
- * to the other file.
- */
- irele(ip); /* We don't need this anymore. */
- fp->f_data = (caddr_t)wip;
- wip->i_count++;
- iunlock(wip);
- return (0);
-
- bad:
- iunlock(wip);
- return (error);
- }
-
- #endif /* NFS */
- #endif /* NFD > 0 */
- SHAR_EOF
- chmod +x 'dev_fd.c'
- fi # end of overwriting check
- echo shar: extracting "'PATCH'" '(544 characters)'
- if test -f 'PATCH'
- then
- echo shar: will not over-write existing file "'PATCH'"
- else
- cat << \SHAR_EOF > 'PATCH'
- *** /tmp/,RCSt1013388 Fri Jul 10 10:31:15 1987
- --- spec_vnodeops.c Sun Jul 5 17:27:24 1987
- ***************
- *** 129,135 ****
- if ((u_int)major(dev) >= nchrdev)
- return (ENXIO);
-
- ! error = (*cdevsw[major(dev)].d_open)(dev,flag, &newdev);
-
- /*
- * If this is an indirect device we need to do the
- --- 132,139 ----
- if ((u_int)major(dev) >= nchrdev)
- return (ENXIO);
-
- ! error = (*cdevsw[major(dev)].d_open)(dev,flag, &newdev,
- ! vpp);
-
- /*
- * If this is an indirect device we need to do the
- SHAR_EOF
- chmod +x 'PATCH'
- fi # end of overwriting check
- # End of shell archive
- exit 0
- --
- Arnold Robbins
- ARPA, CSNET: arnold@emory.ARPA BITNET: arnold@emory
- UUCP: { decvax, gatech, sun!sunatl }!emory!arnold
- ONE-OF-THESE-DAYS: arnold@emory.mathcs.emory.edu
-
- --
-
- Rich $alz "Anger is an energy"
- Cronus Project, BBN Labs rsalz@bbn.com
- Moderator, comp.sources.unix sources@uunet.uu.net
-